/*
 * dtalk_common_utils.h
 *
 *  Created on: 2021年10月14日
 *      Author: guyadong
 */

#ifndef DTALK_DTALK_COMMON_UTILS_H_
#define DTALK_DTALK_COMMON_UTILS_H_

#include "dtalk_item.h"
#include <string_utils.h>
#include <memory>
#include "dtalk_common_constant.h"

namespace dtalk{ 
	/**
	 *
	 * @param mac 设备MAC地址
	 * @return 返回响应通道名
	 */
	inline std::string getAckChannel(const std::string &mac) {
		return mac + ACK_SUFFIX;
	}
	/**
	 * 生成连接频道名
	 * @param mac 设备MAC地址
	 * @return 频道名
	 */
	inline std::string getConnChannel(const std::string &mac){
		return mac + CONNECT_SUFFIX;
	}
	inline item_ptr_type makeQuit() {
		auto item = make_item_shared<CmdItem>();
		item->setField(_jf_name, QUIT_NAME);
		return item;
	}
	inline bool isBack(const item_ptr_type &item) {
		return item != nullptr && BACK_NAME == item->getName();
	}
	inline bool isQuit(const item_ptr_type &item) {
		return item != nullptr && QUIT_NAME == item->getName();
	}
	/**
	 * 归一化输入的{@link JSONObject}对象<br>
	 * 根据{@link CommonConstant#ITEM_FIELD_NAME}或{@link CommonConstant#ITEM_FIELD_PATH}字段的值
	 * 查找是否存在指定的item,如果不存在抛出异常,
	 * 如果没有定义{@link CommonConstant#ITEM_FIELD_NAME}或{@link CommonConstant#ITEM_FIELD_PATH}则抛出异常<br>
	 * 如果 {@link JSONObject}没有指定{@link CommonConstant#ITEM_FIELD_CATALOG}字段，
	 * 则设置为找到的item的对应字段.<br>
	 * 找到的item为{@link BaseOption}对象,且 {@link JSONObject}没有指定{@link CommonConstant#OPTION_FIELD_TYPE}字段，
	 * 则设置为找到的item的对应字段.
	 * @param jsonObject {@link JSONObject}对象
	 * @param start 搜索起始对象
	 * @return 返回归一化的{@code jsonObject}，
	 * 保证有定义{@link CommonConstant#ITEM_FIELD_NAME},{@link CommonConstant#ITEM_FIELD_PATH}字段
	 */
	inline dtalk::json normalize(dtalk::json &j, item_ptr_type &start)
	{
		if (!j.is_null() && start) 
		{
			std::string name;
			std::string path;
			item_ptr_type node;
			if (j.contains(ITEM_FIELD_PATH))
			{
				path = j[ITEM_FIELD_PATH].get<std::string>();
				node = start->findChecked(path);
			}
			else if (j.contains(ITEM_FIELD_NAME))
			{
				name = j[ITEM_FIELD_NAME].get<std::string>();
				node = start->findChecked(name);
			}
			else 
			{
				throw std::invalid_argument("NOT DEFINED " + ITEM_FIELD_PATH + " or " + ITEM_FIELD_NAME);
			}
			if (j.contains(ITEM_FIELD_CATALOG)) {
				auto catalog = j[ITEM_FIELD_CATALOG].get<ItemType>();
				throw_if_msg(node->getCatalog() != catalog, gdface::string_utils::format("MISMATCH CATALOG %s", node->getPath().c_str()));
			}
			else 
			{
				j[ITEM_FIELD_CATALOG] = node->getCatalog();
			}
			if (node->getCatalog() == ItemType::DIT_OPTION) 
			{
				option_ptr_type opt = std::static_pointer_cast<BaseOption>(node);
				if (j.contains(OPTION_FIELD_TYPE))
				{
					throw_if_msg(opt->getType() != j[OPTION_FIELD_TYPE].get<OptionType>(), gdface::string_utils::format("MISMATCH TYPE %s", node->getPath().c_str()));
				}
				else
				{
					j[OPTION_FIELD_TYPE] = opt->getType();
				}
			}
			if (name.empty())
			{
				j[ITEM_FIELD_NAME] = node->getName();
			}
			if (j.contains(ITEM_FIELD_CHILDS))
			{
				for (auto child : j[ITEM_FIELD_CHILDS])
				{
					throw_if_msg(!child.is_object(), gdface::string_utils::format("INVALID JSON FORMAT FOR CHILD OF %s", node->getPath().c_str()));
					normalize(child, node);
				}
			}
		}
		return j;
	}

} /** namespace dtalk */
#endif /* DTALK_DTALK_COMMON_UTILS_H_ */
